from dataclasses import InitVar, dataclass, field, fields
from typing import ClassVar, List, Optional, Set
from enum import Enum, auto
from datetime import date


@dataclass
class ClubMember:

    name: str
    # use default_factory to create mutable default value
    guests: list = field(default_factory=list)
    # List[str] means 'a list of str', List[str] is a generic type definition
    guests_str: List[str] = field(default_factory=list)
    # default value for athlete, repr not include this field
    athlete: bool = field(default=False, repr=False)
    # wrong example
    # guests: list = []


@dataclass
class HackerClubMember(ClubMember):

    # ClassVar leverages the generics[] notation
    # to set the type of variable and also declare it a class attibute
    # 也就是类变量, static变量的意思
    # dataclass decorator will not generate instance field for this attribute
    all_handles: ClassVar[Set[str]] = set()

    handle: str = ''

    # post method running after __init__ method to do more works
    def __post_init__(self):
        cls = self.__class__
        if self.handle == '':
            self.handle = self.name.split()[0]
        if self.handle in cls.all_handles:
            msg = f'handle {self.handle!r} already exists'
            raise ValueError(msg)
        cls.all_handles.add(self.handle)


@dataclass
class C:
    """
     Which demonstrate that InitVar usage


    [extended_summary]
    """
    i: int
    j: int = 0
    # init-only variable, are added parameters to the generated __init__method
    # and passed to the __post_init__method to use
    # InitVar will prevent @dataclass from treating database as a regular field
    # will not be set as an instance attribute, and the field will be one of arguments
    # that the generated__init__ will accpet, and will be passed to __post_init__
    database: InitVar[str] = None

    def __post_init__(self, database: str):
        if self.j == 0 and database is not None:
            self.j = len(database)


class ResourceType(Enum):
    BOOK = auto()
    EBOOK = auto()
    VIDEO = auto()


@dataclass
class Resource:
    """
     Media resource description
    """
    # the only required field
    identifier: str
    title: str = '<untitled>'
    creaters: List[str] = field(default_factory=list)
    # the value of date can be a datetime.date instance or None
    date: Optional[date] = None
    type: ResourceType = ResourceType.BOOK
    description: str = ''
    language: str = ''
    subjects: List[str] = field(default_factory=list)

    def __repr__(self) -> str:
        cls = self.__class__
        cls_name = cls.__name__
        indent = ' ' * 4
        res = [f'{cls_name}(']
        for f in fields(cls):
            value = getattr(self, f.name)
            res.append(f'{indent}{f.name} = {value!r},')

        res.append(')')
        return '\n'.join(res)


if __name__ == "__main__":
    c = C(10, database='new database')
    print(c)
    description = 'Improving the design of existing code'
    book = Resource('978-0-13-475759-9', 'Refactoring, 2nd Editon',
                    ['Martin Fowler', 'Kent Beck'], date(2018, 11, 19),
                    ResourceType.BOOK, description, 'EN', ['computer programming', 'OOP'])
    print(book)
